<template>
	<div>
    <!-- 面包屑导航 -->
    <el-breadcrumb separator-class="el-icon-arrow-right">
      <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>权限管理</el-breadcrumb-item>
      <el-breadcrumb-item>角色列表</el-breadcrumb-item>
    </el-breadcrumb>
		<!-- 卡片区域 -->
		<el-card>
			<!-- 按钮 -->
			<el-row>
				<el-col>
					<el-button type="primary" @click="addDialogVisible = true">添加角色</el-button>
				</el-col>
			</el-row>
			<!-- 表格区域 -->
			<el-table :data="rolesList" border stripe>
				<!-- 展开列 -->
				<el-table-column type="expand">
					<template slot-scope="scope">
						<el-row :class="[index1 === 0 ? 'bdtop': '','vcenter','bdbottom']" v-for="(item1,index1) in scope.row.children" :key="item1.id">
							<!-- 一级权限 -->
							<el-col :span="5">
								<el-tag closable @close="removeRightById(scope.row,item1.id)">{{item1.authName}}</el-tag>
								<i class="el-icon-caret-right"></i>
							</el-col>
							<!-- 二级和三级权限 -->
							<el-col :span="19">
								<el-row class="vcenter" :class="[index2 === 0 ? '' : 'bdtop']" v-for="(item2,index2) in item1.children" :key="item2.id">
									<!-- 二级权限 -->
									<el-col :span="6">
										<el-tag closable type="success" @close="removeRightById(scope.row,item2.id)">{{item2.authName}}</el-tag>
										<i class="el-icon-caret-right"></i>
									</el-col>
									<!-- 三级权限 -->
									<el-col :span="18">
										<el-tag closable type="warning" v-for="(item3,index3) in item2.children" :key="item3.id" @close="removeRightById(scope.row,item3.id)">{{item3.authName}}</el-tag>
									</el-col>
								</el-row>
							</el-col>
						</el-row>
					</template>
				</el-table-column>
				<!-- 索引列 -->
				<el-table-column type="index" label="#"></el-table-column>
				<el-table-column prop="roleName" label="角色名称"></el-table-column>
				<el-table-column prop="roleDesc" label="角色描述"></el-table-column>
				<el-table-column label="操作" width="500px">
					<template slot-scope="scope">
						<el-button size="mini" type="primary" icon="el-icon-edit" @click="showEditDialog(scope.row.id,scope.row.roleName)">编辑</el-button>
						<el-button size="mini" type="danger" icon="el-icon-delete" @click="removeRoleById(scope.row.id)">删除</el-button>
						<el-button size="mini" type="warning" icon="el-icon-setting" @click="showSetRightDialog(scope.row)">分配权限</el-button>
					</template>
				</el-table-column>
			</el-table>
		</el-card>
		<!-- 添加角色的对话框 -->
		<el-dialog title="添加角色" :visible.sync="addDialogVisible" width="50%" @close="roleDialogClose">
				<span>
					<el-form :model="addForm" :rules="addFormRules" ref="addFormRef" label-width="100px">
						<el-form-item label="角色名称" prop="roleName">
							<el-input v-model="addForm.roleName"></el-input>
						</el-form-item>
						<el-form-item label="角色描述" prop="roleDesc">
							<el-input v-model="addForm.roleDesc"></el-input>
						</el-form-item>
					</el-form>
				</span>
				<span slot="footer" class="dialog-footer">
					<el-button @click="addDialogVisible = false">取 消</el-button>
					<el-button type="primary" @click="addRole">确 定</el-button>
				</span>
		</el-dialog>
		<!-- 编辑角色的对话框 -->
		<el-dialog title="编辑角色" :visible.sync="editDialogVisible" width="50%" @close="editDialogClose">
				<span>
					<el-form :model="editForm" :rules="editFormRules" ref="editFormRef" label-width="100px">
					<el-form-item label="ID">
						<el-input v-model="editForm.roleId" disabled></el-input>
					</el-form-item>
					<el-form-item label="角色名称" prop="roleName">
						<el-input v-model="editForm.roleName"></el-input>
					</el-form-item>
					<el-form-item label="角色描述">
						<el-input v-model="editForm.roleDesc"></el-input>
					</el-form-item>
					</el-form>
				</span>
				<span slot="footer" class="dialog-footer">
					<el-button @click="editDialogVisible = false">取 消</el-button>
					<el-button type="primary" @click="editRoleInfo">确 定</el-button>
				</span>
		</el-dialog>
		<!-- 分配权限的对话框 -->
		<el-dialog title="分配权限" :visible.sync="setRightDialogVisible" width="50%" @close="setRightDialogClose">
			<el-tree :data="rightslist" :props="treeProps" show-checkbox default-expand-all node-key="id" :default-checked-keys="defKeys" ref="tree"></el-tree>
				<span slot="footer" class="dialog-footer">
					<el-button @click="setRightDialogVisible = false">取 消</el-button>
					<el-button type="primary" @click="allotRights">确 定</el-button>
				</span>
		</el-dialog>

	</div>
</template>

<script>
export default {
	data(){
		// 验证角色名称的规则
		var checkRoleName = (rule, value, callback) => {
			// 角色名称的正则表达式
			const regRoleName = /^[\u4e00-\u9fa5_a-zA-Z0-9]+$/
        if (regRoleName.test(value)) {
          return callback()
        } 
				callback(new Error('请输入合法的角色名称'))
		}
		return {
			// 所有角色列表信息
			rolesList:[],
			// 控制添加角色对话框的显示和隐藏
			addDialogVisible:false,
			// 保存新添加角色的信息
			addForm:{
				roleName:'',
				roleDesc:''
			},
			// 添加角色名称的合法规则
			addFormRules:{
				roleName:[
					{required: true, message:'请输入角色名称',trigger: 'blur'},
					{validator: checkRoleName,trigger: 'blur'}
				]
			},
			// 控制编辑角色对话框的显示和隐藏
			editDialogVisible:false,
			// 点击编辑角色接收查询的角色信息
			editForm:{},
			// 编辑角色名称的合法规则
			editFormRules:{
				roleName:[
					{required: true,message: '请输入角色名称',trigger: 'blur'},
					{validator: checkRoleName,trigger: 'blur'}
				]
			},
			// 控制分配权限对话框的显示和隐藏
			setRightDialogVisible:false,
			// 所有权限信息
			rightslist:[],
			// el-tree读取的数据结构
			treeProps:{
				children:'children',
				label:'authName'
			},
			// 默认选中的节点ID值数组
			defKeys:[],
			// 当前即将分配角色的id
			roleId:''
		}	
	},
	created(){
		this.getRolesList()
	},
	methods:{
		// 获取角色列表数据
		async getRolesList(){
			const {data: res} = await this.$http.get('roles')
			if(res.meta.status !== 200){
				this.$message.error('用户角色获取失败')
			}
			console.log(res);
			this.rolesList = res.data
			console.log(this.rolesList);
		},
		// 监听添加角色对话框关闭重置事件
		roleDialogClose(){
			// 此处重置由于resetFields重置form会有bug，因此加一个定时器过渡掉文字瞬间回复的问题
			setTimeout(() => {
				this.$refs.addFormRef.resetFields()
			}, 100);
		},
		// 监听点击确定，添加角色事件
		addRole(){
			this.$refs.addFormRef.validate(async valid => {
				if(!valid) return
				// 发起添加角色信息的请求
				const {data: res} = await this.$http.post('roles',this.addForm)
				if(res.meta.status !== 201){
					return this.$message.error('添加角色失败')
				}
				this.$message.success('添加角色成功')
				this.getRolesList()
				this.addDialogVisible = false
			})			
		},
		// 监听点击编辑，请求角色信息事件
		async showEditDialog(id){
			// 显示对话框
			this.editDialogVisible = true
			const {data: res} = await this.$http.get('roles/' + id)
			if(res.meta.status !== 200){
				return this.$message.error('查询角色信息失败')
			}
				this.editForm = res.data
		},
		// 监听关闭修改角色对话框重置数据
		editDialogClose(){
		// 此处重置由于resetFields重置form会有bug，因此加一个定时器过渡掉文字瞬间回复的问题
			setTimeout(() => {
				this.$refs.editFormRef.resetFields()
			}, 100);
		},
		// 修改角色信息并提交
		editRoleInfo(){
			this.$refs.editFormRef.validate(async valid => {
				if(!valid) return
				// 发起修改角色的请求
				const {data: res} = await this.$http.put('roles/' + this.editForm.roleId,{
					roleName:this.editForm.roleName,
					roleDesc:this.editForm.roleDesc
				})
				// console.log(res);
				if(res.meta.status !== 200){
					this.$message.error('角色信息更新失败')
				}
				// 关闭对话框
				this.editDialogVisible = false
				// 刷新数据列表
				this.getRolesList()
				// 弹框信息提示
				this.$message.success('更新角色信息成功')
				
			})
		},
		// 监听删除角色信息
		async removeRoleById(id){
			 const confirmResult =  await this.$confirm('此操作将永久删除该角色, 是否继续?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
				}).catch(err => err)
				// console.log(confirmResult);
				// 如果用户点击确认删除，则返回字符串confirm
				// 如果用户点了取消删除，则返回字符串cancel（注意此处必须用catch捕获失败的处理，不然会报错）	
				// console.log(confirmResult);
				// 因此可以对返回的是不是confirm来判定用户点击的选择
				if(confirmResult !== 'confirm'){
					return this.$message('已取消删除')
				}
				const {data: res} = await this.$http.delete('roles/' + id)
				if(res.meta.status !== 200){
					this.$message.error('删除角色信息失败')
				}
				this.$message.success('该角色信息已删除')
				this.getRolesList()
		},
		// 监听点击关闭权限按钮的事件
		async removeRightById(role,rightId){
			const confirmResult = await this.$confirm('此操作将永久删除该权限, 是否继续?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
				}).catch(err => err)
			if(confirmResult !== 'confirm'){
				return this.$message('已取消权限删除')
			}			
			const {data: res} = await this.$http.delete(`roles/${role.id}/rights/${rightId}`)
			if(res.meta.status !== 200){
				return this.$message.error('删除权限失败')
			}
			this.$message.success('删除权限成功')
			// 删除完权限后不能用this.getRolesList()重新获取列表数据，因为会导致刷新
			// 因为返回数据内的data数据就是最新的权限数据，因此下面直接赋值更新权限数据（因为渲染的数据是双向绑定的）
			role.children = res.data
		},
		// 展示分配权限的对话框
		async showSetRightDialog(role){
			// 获取所有权限的列表(树状)
			const {data: res} = await this.$http.get('rights/tree')
			if(res.meta.status !== 200){
				return this.$message.error('获取权限信息失败')
			}
			// 把获取到的所有权限数据存储到rightslist中
			this.rightslist = res.data
			console.log(this.rightslist);
			// 显示对话框
			this.setRightDialogVisible = true
			// 相应角色的id赋值给roleId，后续要用
			this.roleId = role.id
			// 点击分配权限同时，把该行数据的三级权限id通过调用递归函数gerLeafKeys赋值给defKeys数组
			this.getLeafKeys(role,this.defKeys)
			// console.log(this.defKeys);
		},
		// 通过递归的形式获取角色下的所有三级权限的id，保存到defKeys中
		getLeafKeys(node,arr){
			// 注意当node不包含children属性，则是三级节点
			if(!node.children){
				return arr.push(node.id)
			}
			node.children.forEach(item => {
				this.getLeafKeys(item,arr)
			})
		},
		// 点击为角色分配权限
		async allotRights(){
			// 把所有勾选和半勾选的节点汇总
			const keys = [...this.$refs.tree.getCheckedKeys(),...this.$refs.tree.getHalfCheckedKeys()]
			// 传给后台的参数要求逗号分隔字符串
			const idStr = keys.join(',')
			// 角色相应权限的授权
			const {data: res} = await this.$http.post(`roles/${this.roleId}/rights`,{
				rids:idStr
			})
			if(res.meta.status !== 200){
				return this.$message.error('分配权限失败')
			}
			this.$message.success('分配权限成功')
			// 更新数据
			this.getRolesList()
			// 关闭对话框
			this.setRightDialogVisible = false
		},
		
		// 监听分配权限对话框关闭事件
		setRightDialogClose(){
			// 关闭对话框时清空defKeys内的数据
			this.defKeys = []
		}
	}
}
</script>

<style lang="less" scoped>
	.el-tag{
		margin: 7px;
	}
	.vcenter{
		display: flex;
		align-items: center;
	}
	.bdbottom{
		border-bottom: 1px solid #eee;
	}
	.bdtop{
		border-top: 1px solid #eee;
	}
</style>